package com.cupdata.zicon.jdk7concurrent.chapter2;


/**
 * If a thread (A) is executing
a synchronized method and another thread (B) wants to execute other synchronized
methods of the same object, it will be blocked until the thread (A) ends. But if threadB has
access to different objects of the same class, none of them will be blocked.
 
 You can use recursive calls with synchronized methods. As the thread has access to
the synchronized methods of an object, you can call other synchronized methods
of that object, including the method that is executing. It won't have to get access to the
synchronized methods again.
 
 Static methods have a different behavior. Only one execution thread will access one of the static
methods declared with the synchronized keyword, but another thread can access other nonstatic
methods of an object of that class. You have to be very careful with this point, because
two threads can access two different synchronized methods if one is static and the other one
is not. If both methods change the same data, you can have data inconsistency errors.
 
 * 
 * @author SunYabing
 *
 */
public class SynchronizingMethodTest {

	//@Test
	public static void  main(String[] args) {
		Account account = new Account();
		account.setBalance(1000);

		Company company = new Company(account);
		Thread companyThread = new Thread(company);

		Bank bank = new Bank(account);
		Thread bankThread = new Thread(bank);

		System.out.printf("Account : Initial Balance: %f\n",
				account.getBalance());
		// Start the threads.
		companyThread.start();
		bankThread.start();

		
		
		try {
			companyThread.join();
			bankThread.join();
			System.out.printf("Account : Final Balance: %f\n",
					account.getBalance());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}

class Account {
	private  //static
	    double balance;

	public double getBalance() {
		return balance;
	}

	public void setBalance(double balance) {
		this.balance = balance;
	}

	public  synchronized// static 
	       void addAmount(double amount) {
		
		double tmp = balance;
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		tmp += amount;
		balance = tmp;
		
	}

	public synchronized void subtractAmount(double amount) {
		double tmp = balance;
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		tmp -= amount;
		balance = tmp;
	}

}

class Bank implements Runnable {
	private Account account;

	public Bank(Account account) {
		this.account = account;
	}

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			account.subtractAmount(1000);
		}
	}
}

class Company implements Runnable {
	private Account account;

	public Company(Account account) {
		this.account = account;
	}

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			account.addAmount(1000);
		}
	}
}
